<?php

namespace Lib\Route;

use Lib\Util\Util;


/**
 * Route  路由类
 * 
 * 主要用于路由的业务分发处理，并且验证参数的有效性
 * 
 * @author: Xialj
 * @date: 2019-08-24
 * @version: v1.0
 * @email: m17269@163.com
 */
class Route
{
    // 等待接收的参数数组
    private $httpRequest = null;

    /**
     * 构造方法
     * 用于路由分发的构造
     */
    public function __construct($httpRequest)
    {
        global $CONFIG;
        // 检测项目是否存在
        if (empty($httpRequest['pro'])) {
            $var = [
                'code' => $CONFIG['others']['code']['nullDataCode'],
                'msg' => '请求参数 pro 不存在'
            ];
            Util::send($var);
        }
        // 再次加载
        elseif (empty($httpRequest['rid'])) {
            $var = [
                'code' => $CONFIG['others']['code']['nullDataCode'],
                'msg' => '请求参数 rid 不存在'
            ];
            Util::send($var);
        }
        $this->checkMustSubmitParams($httpRequest);
        $this->httpRequest = $httpRequest;
    }

    /**
     * 执行方法 
     * 将验证通过的方法进行执行，分发完成
     */
    public function execute(): void
    {
        global $CONFIG;
        // 提取关联数组
        $arRequest = $this->extractAssociativeArray();

        $proName = $arRequest['pro'];
        $moduleName = $arRequest['m'];     // 获取ModuleName
        $controllerName = $arRequest['c']; // 获取Controller
        $actionName = $arRequest['a'];     // 请求方式
        // 设置模块 控制器 类文件 的完整性
        $modulePath = $CONFIG['X']['pro'][$proName];                        //设置请求的模块名称
        $controllerFileName = $controllerName . 'Controller.php';                     //设置请求的控制器文件名称      
        $pathFileName = $modulePath . '/' . $moduleName . "/C/" . $controllerFileName;  // 加载类文件的完整路径
        
        require_once($pathFileName);   //加载控制器类文件
        // 获取对应的类名
        $controllerName .= 'Controller';
        if (!method_exists($controllerName, $actionName)) {                  //是否存在controller
            $var = [
                'code' => $CONFIG['others']['UtilCode']['controllerNotFound'],
                'msg' => "请求 $actionName 不存在"
            ];
            Util::send($var);
        }
        // 开始产生类，并执行对应的方法
        $controller = new $controllerName($arRequest);
        $controller->$actionName($arRequest);
    }

    /**
     * 检查必须客户端请求必须提交的参数
     * @param httpRequest 请求参数集合，进行拦截过滤
     * @return 无返回信息
     */
    private function checkMustSubmitParams($httpRequest): void
    {
        global $CONFIG;
        global $ROUTE;
        $pro = $httpRequest['pro'];  // 获取请求pro
        if (!array_key_exists($pro, $ROUTE)) { // 检查pro是否存在于route表中
            $var = [
                'code' => $CONFIG['others']['UtilCode']['proNotFound'],
                'msg' => '请求pro：' . $pro . '不存在'
            ];
            Util::send($var);
        }

        $rid = $httpRequest['rid'];  // 获取请求rid
        if (!array_key_exists($rid, $ROUTE[$pro])) { // 检查rid是否存在于route表中
            $var = [
                'code' => $CONFIG['others']['UtilCode']['ridNotFound'],
                'msg' => '请求rid：' . $rid . '不存在'
            ];
            Util::send($var);
        }

        foreach ($ROUTE[$pro][$rid]['isset'] as $param) {                    //检查每个参数是否都设置了
            if (!isset($httpRequest[$param])) {
                $var = [
                    'code' => $CONFIG['others']['UtilCode']['issetNotFound'],
                    'msg' => '请求rid：' . $rid . '中不存在' . $param . '参数'
                ];
                Util::send($var);
            }
        }
        foreach ($ROUTE[$pro][$rid]['empty'] as $param) {                    //检查每个参数是否都不为空
            if (empty($httpRequest[$param])) {
                $var = [
                    'code' => $CONFIG['others']['UtilCode']['emptyNotFound'],
                    'msg' => '请求rid：' . $rid . '中的请求参数' . $param . '为empty'
                ];
                Util::send($var);
            }
        }
        foreach ($ROUTE[$pro][$rid]['string'] as $param) {                   //检查每个参数是否都是字符串格式
            if (!is_string($httpRequest[$param])) {
                $var = [
                    'code' => $CONFIG['others']['UtilCode']['stringNotFound'],
                    'msg' => '请求rid：' . $rid . '中的请求参数' . $param . '不为字符串格式'
                ];
                Util::send($var);
            }
        }
        foreach ($ROUTE[$pro][$rid]['numeric'] as $param) {                  //检查每个参数是否都是数字格式
            if (!is_numeric($httpRequest[$param])) {
                $var = [
                    'code' => $CONFIG['others']['UtilCode']['nullDataCode'],
                    'msg' => '请求rid：' . $rid . '中的请求参数' . $param . '不为数字格式'
                ];
                Util::send($var);
            }
        }
    }

    /**
     * 过滤参数,可过滤单值和数组
     * @param param 待过滤的参数
     * @return value|param 经过处理的数组变量 | 经过处理的特殊字符
     */
    private function filteParam($param)
    {
        if (!is_array($param)) {                       // 判断变量是否为数组
            $value = trim(htmlspecialchars($param));   // 去掉html特殊字符和首尾空格
            return $value;                             // 经过处理的特殊字符
        } else {
            foreach ($param as &$item) {               // 按引用修改数组每个元素
                $item = trim(htmlspecialchars($item)); // 去掉html特殊字符和首尾空格
            }
            unset($item);                              // 循环结束后最后一个元素的引用还会保留，使用unset去除
            return $param;                             // 经过处理的数组变量
        }
    }

    /**
     * 提取关联数组
     */
    private function  extractAssociativeArray(): ?array
    {
        global $ROUTE;
        $pro_flag = false;
        foreach ($this->httpRequest as $key => $value) {
            if (!$pro_flag) {
                // 如果项目注册 则执行
                if ($key == 'pro') {
                    $pro_flag = true;
                    $arSysReqRoute = $ROUTE[$value];
                    $arRequestParams['pro'] = $value;
                    continue;
                } else {
                    return null;
                }
            }

            if ($pro_flag) {
                $key =  $this->filteParam($key);
                $value =  $this->filteParam($value);
                if ($key == 'rid') {  // 如果key==rid，进行参数封装
                    $arRequestParams['rid'] = $value;                               //设置rid
                    $arRequestParams['m'] = $arSysReqRoute[$value]['m'];              //设置模块
                    $arRequestParams['c'] = $arSysReqRoute[$value]['c'];              //设置控制器
                    $arRequestParams['a'] = $arSysReqRoute[$value]['a'];              //设置行为
                } else {
                    $arRequestParams[$key] = $value;                                  //为请求参数关联数组设置数值
                }
            }
        }
        return $arRequestParams;
    }
}
